package hku.cs.lunuo.launch;

import hku.cs.instrument.TestCaseInstrument;
import hku.cs.lunuo.LunuoPlugin;
import hku.cs.lunuo.nature.SootNature;
import hku.cs.lunuo.page.PreferenceConstant;
import hku.cs.lunuo.util.LunuoLibHelper;
import hku.cs.lunuo.views.RankView;
import hku.cs.tools.DirTools;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.Vector;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.junit.launcher.JUnitLaunchConfigurationDelegate;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jdt.launching.IRuntimeClasspathEntry;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;

import soot.Scene;

public class LunuoLaunchConfigDelegate extends JUnitLaunchConfigurationDelegate {

	public final static String ID = "hku.cs.lunuo.launch.lunuoLaunchConfigType";

	@Override
	public void launch(final ILaunchConfiguration configuration,
			final String mode, final ILaunch launch,
			final IProgressMonitor monitor) throws CoreException {
		
		final IJavaProject javaProject = getJavaProject(configuration);
		final ILaunchConfigurationWorkingCopy lunuoConfig = configuration
				.getWorkingCopy();

		String mainClassName = lunuoConfig.getAttribute(
				IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME, "xx");
		String launchName = lunuoConfig.getName();
		final String arg1 = getInstruResultPath(javaProject) + launchName + "_"
				+ mainClassName + DirTools.SEPERATOR;

		// try {
		// new Thread(new Runnable(){
		// public void run(){
		// TestCaseInstrument tci = new TestCaseInstrument(arg1,
		// getInstruBinPath(javaProject).toOSString());
		// String classpath =
		// LunuoLibHelper.urlsToString(getSootClassPathForTestModify(javaProject));
		// Scene.v().setSootClassPath(classpath);
		// try {
		// tci.modifyTests(new TestSearcher().findAll(javaProject, monitor));
		// } catch (FileNotFoundException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// } catch (JavaModelException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// } catch (IOException e) {
		// // TODO Auto-generated catch block
		// e.printStackTrace();
		// }
		// }
		// }).start();
		// }catch (Exception ex) {
		//
		// }

		// compute the classpath for profile and modify test
		final Vector<String> userClasspath = toMemento(JavaRuntime
				.computeUnresolvedRuntimeClasspath(javaProject));
		final Vector<String> lunuoWithClasspath = toMemento(getLunuoWithLibClasspath());
		final Vector<String> instrClasspath = toMemento(getInstruClasspath(getInstruBinPath(javaProject)));
		List<String> classpathForProfile = configuration.getAttribute(
				IJavaLaunchConfigurationConstants.ATTR_CLASSPATH,
				(List<String>) null);

		if (classpathForProfile == null) {
			classpathForProfile = lunuoWithClasspath;
			classpathForProfile.addAll(0, userClasspath);
			classpathForProfile.addAll(0, instrClasspath);
		} else {
			classpathForProfile.addAll(0, lunuoWithClasspath);
			classpathForProfile.addAll(0, userClasspath);
			classpathForProfile.addAll(0, instrClasspath);
		}

		lunuoConfig.setAttribute(
				IJavaLaunchConfigurationConstants.ATTR_CLASSPATH,
				classpathForProfile);
		lunuoConfig
				.setAttribute(
						IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH,
						false);
		final String rankAlg = lunuoConfig.getAttribute(
				LunuoLaunchConstant.ATT_RANK_NAME, LunuoPlugin.getDefault()
						.getPreferenceStore().getString(
								PreferenceConstant.ALG_KEY));

		try {
			new Thread(new Runnable() {
				public void run() {
					try {
						Display.getDefault().asyncExec(new Runnable() {
							public void run() {
								IViewPart viewer = null;
								try {
									IWorkbench workbench = PlatformUI
											.getWorkbench();
									IWorkbenchWindow window = workbench
											.getActiveWorkbenchWindow();
									try {
										viewer = window
												.getActivePage()
												.showView(
														RankView.VIEW_ID,
														null,
														IWorkbenchPage.VIEW_VISIBLE);
									} catch (PartInitException e) {
										// TODO Auto-generated catch block
										e.printStackTrace();
									}
									new LunuoAfterLaunchDelegate(arg1,
											javaProject,
											getSootClassPath(javaProject),
											viewer,
											rankAlg	);
									// ----------do modify first
									
									
									IProject project = javaProject.getProject();
									if(!project.hasNature(SootNature.NATURE_ID)){
										LunuoPlugin.getDefault().addSootNature(project, false);
									}
										
									soot.G.reset();
									String classpath = LunuoLibHelper
											.urlsToString(getSootClassPath(javaProject));
									Scene.v().setSootClassPath(classpath);
									new TestCaseInstrument().modifyTests(
											new TestSearcher().findAll(
													javaProject, monitor),
											arg1, getInstruBinPath(javaProject)
													.toOSString());
									
									// ----------then start junit
									LunuoLaunchConfigDelegate.super.launch(
											lunuoConfig, mode, launch, monitor);

								} catch (CoreException e) {
									e.printStackTrace();
									return;
								} catch (Exception ex) {
									ex.printStackTrace();
								}
							}
						});
					} catch (Exception ex) {
						ex.printStackTrace();
					}
				}
			}).start();
		} catch (Exception ex) {

			// CactusPlugin.displayErrorMessage(
			// CactusMessages.getString(
			// "CactusLaunch.message.containerManager.error"),
			// ex.getMessage(),
			// null);
		}
	}

	private IPath getInstruResultPath(IJavaProject javaProject) {
		IPath path = LunuoPlugin.METADIR.append(javaProject.getElementName()
				+ "/");
		return path;
	}

	private IPath getInstruBinPath(IJavaProject javaProject) {
		IPath path = null;
		try {
			path = LunuoPlugin.METADIR.append(javaProject.getOutputLocation()
					.toString()
					+ "/");
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		return path;
	}

	// private URL[] getSootClassPathForTestModify(IJavaProject javaProject){
	// Set<URL> us = getSootClassPath(javaProject);
	// try {
	// us.add(getInstruBinPath(javaProject).toFile().toURI().toURL());
	// } catch (MalformedURLException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// URL[] array = new URL[us.size()];
	// us.toArray(array);
	// return array;
	// }

	// private URL[] getSootClassPathForProfile(IJavaProject javaProject){
	// Set<URL> us = getSootClassPath(javaProject);
	// try {
	// String uriString = ResourcesPlugin.getWorkspace().getRoot().getFile(
	// javaProject.getOutputLocation()).getLocationURI()
	// .toString()
	// + "/";
	// us.add(new URI(uriString).toURL());
	// } catch (MalformedURLException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// } catch (URISyntaxException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// } catch (JavaModelException e) {
	// // TODO Auto-generated catch block
	// e.printStackTrace();
	// }
	// URL[] array = new URL[us.size()];
	// us.toArray(array);
	// return array;
	// }

	private URL[] getSootClassPath(IJavaProject javaProject) {
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IClasspathEntry[] cp;
		try {
			cp = javaProject.getResolvedClasspath(true);
			Set<URL> urls = new HashSet<URL>();

			for (IClasspathEntry entry : cp) {
				if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
					URI uri = new URI(workspace.getRoot().getFile(
							entry.getPath()).getLocationURI().toString()
							+ "/");
					urls.add(uri.toURL());
				} else {
					File file = entry.getPath().toFile();
					URL url = file.toURI().toURL();
					urls.add(url);
				}
			}
			IClasspathEntry[] lcp = getLunuoWithLibClasspath();
			for (IClasspathEntry entry : lcp) {
				File file = entry.getPath().toFile();
				URL url = file.toURI().toURL();
				urls.add(url);
			}

			URL[] array = new URL[urls.size() + 1];
			String uriString = workspace.getRoot().getFile(
					javaProject.getOutputLocation()).getLocationURI()
					.toString()
					+ "/";
			array[0] = new URI(uriString).toURL();
			Iterator<URL> i = urls.iterator();
			int j = 1;
			while (i.hasNext()) {
				array[j] = i.next();
				j++;
			}

			return array;

		} catch (JavaModelException e) {
			e.printStackTrace();
			return new URL[0];
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return new URL[0];
		} catch (URISyntaxException e) {
			e.printStackTrace();
			return new URL[0];
		} catch (CoreException e) {
			e.printStackTrace();
			return new URL[0];
		}
	}

	private IClasspathEntry[] getInstruClasspath(IPath binPath) {
		IClasspathEntry[] result = new IClasspathEntry[1];
		result[0] = JavaCore.newLibraryEntry(binPath, null, null);
		return result;
	}

	private Vector<String> toMemento(IClasspathEntry[] theEntries) {
		Vector<String> result = new Vector<String>();
		for (int i = 0; i < theEntries.length; i++) {
			try {
				result.add(JavaRuntime.newArchiveRuntimeClasspathEntry(
						theEntries[i].getPath()).getMemento());
			} catch (CoreException e) {
				//               
			}
		}
		return result;
	}

	private Vector<String> toMemento(IRuntimeClasspathEntry[] theEntries) {
		Vector<String> result = new Vector<String>();
		for (int i = 0; i < theEntries.length; i++) {
			try {
				result.add(theEntries[i].getMemento());
			} catch (CoreException e) {
				// Do nothing
			}
		}
		return result;
	}class MyJob1 extends Job{

		IJavaProject javaProject;

		public MyJob1(String name, IJavaProject p) {
			super(name);
			this.javaProject = p;
			// TODO Auto-generated constructor stub
		}

		@Override
		protected IStatus run(IProgressMonitor monitor) {
			
			return Status.OK_STATUS;
		}
		
	}

	// protected IClasspathEntry[] getLunuoOnlyClasspath() throws CoreException
	// {
	// return LunuoLibHelper.getClientSideOnlyEntries();
	// }

	private IClasspathEntry[] getLunuoWithLibClasspath() throws CoreException {
		return LunuoLibHelper.getClientSideWithLibEntries();
	}
	
}
